import time
from abc import abstractmethod
from functools import wraps

from cv.cv import Template
from cv.error import TargetNotFoundError
from cv.settings import Settings as ST
from lib.logger import LoggerManager

lg = LoggerManager.get_logger(name="PicBase", write_to_file=False)


def delay(func):
    """
    After each step, stay for a certain amount of time
    """
    delay_time = 1

    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        time.sleep(delay_time)
        return result
    return wrapper


class PicBase:
    """基础图像识别类，不依赖于具体的设备驱动"""

    def loop_find(self, query, timeout=ST.FIND_TIMEOUT, threshold=None, interval=0.5, intervalfunc=None):
        """
        循环在屏幕中搜索图像模板直到超时

        参数:
            query: 需要在截图中查找的图像模板
            screen: 屏幕截图数据
            timeout: 查找图像模板的超时时间
            threshold: 匹配阈值
            interval: 每次尝试查找图像模板之间的休眠间隔
            intervalfunc: 在未成功找到图像模板后执行的函数
        """
        lg.info("Try finding: %s", query.filepath)
        start_time = time.time()

        while True:
            screen = self.take_screenshot()

            if screen is None:
                lg.warning("Screen is None, may be locked")
            else:
                if threshold:
                    query.threshold = threshold
                match_pos = query.match_in(screen)
                if match_pos:
                    return match_pos

            if time.time() - start_time > timeout:
                raise TargetNotFoundError('Picture %s not found in screen' % query)

            if intervalfunc is not None:
                intervalfunc()

            time.sleep(interval)

    @abstractmethod
    def get_screen_resolution(self) -> tuple[int, int]:
        """获取屏幕分辨率（抽象方法）"""
        raise NotImplementedError

    @abstractmethod
    def take_screenshot(self, format: str = "opencv"):
        """获取屏幕截图（抽象方法）"""
        raise NotImplementedError

    @delay
    def pic_exists(self, img_path: str):
        """
        判断图像是否存在
        :param img_path: 图像的路径
        :return: 存在返回位置坐标，不存在返回False
        """
        v = Template(img_path)
        try:
            if v.resolution == ():
                v.resolution = self.get_screen_resolution()
            pos = self.loop_find(v, timeout=ST.FIND_TIMEOUT_TMP)
        except TargetNotFoundError:
            return False
        else:
            return pos

    @delay
    def pic_touch(self, img_path: str, *, frequency: int = 1):
        """
        图片点击操作
        :param img_path: 图像的路径
        :param frequency: 点击次数
        :return: 返回点击位置的坐标
        """
        v = Template(img_path)
        if v.resolution == ():
            v.resolution = self.get_screen_resolution()

        try:
            pos = self.loop_find(v, timeout=ST.FIND_TIMEOUT)
            if frequency == 1:
                self.click(*pos)
            elif frequency == 2:
                self.double_click(*pos)

        except TargetNotFoundError as e:
            raise e

        return pos

    @delay
    def pic_wait(self, img_path: str, *, timeout=5, interval=0.5, intervalfunc=None):
        """
        等待设备屏幕上匹配到指定模板图像

        :param img_path: 图像的路径
        :param timeout: 等待匹配的超时时间，默认为None，此时使用 ST.FIND_TIMEOUT
        :param interval: 每次尝试查找匹配的时间间隔（秒）
        :param intervalfunc: 每次未找到匹配后调用的函数
        :raise TargetNotFoundError: 超时后仍未找到目标时抛出
        :return: 匹配目标的坐标
        :platforms: Android, Windows, iOS
        :示例:

            wait("1.png")  # ST.FIND_TIMEOUT后超时
            # 每3秒查找一次模板，120秒后超时
            wait("2.png", timeout=120, interval=3)

            你可以指定一个回调函数在每次搜索目标失败时执行::

            def notfound():
               print("未找到目标")
            wait(Template(r"tpl1607510661400.png"), intervalfunc=notfound)
        """
        v = Template(img_path)
        if v.resolution == ():
            v.resolution = self.get_screen_resolution()
        timeout = timeout or ST.FIND_TIMEOUT
        try:
            pos = self.loop_find(v, timeout=timeout, interval=interval, intervalfunc=intervalfunc)

        except TargetNotFoundError as e:
            raise e
        return pos

    @delay
    def pic_long_touch(self, img_path: str, *, timeout=5, duration=0.5):
        """
        图片长按操作
        :param img_path: 图像的路径
        :param timeout: 查找图像的超时时间
        :param duration: 长按持续时间（秒）
        :return: 无
        """
        v = Template(img_path)
        if v.resolution == ():
            v.resolution = self.get_screen_resolution()

        try:
            timeout = timeout or ST.FIND_TIMEOUT
            pos = self.loop_find(v, timeout=timeout)
            self.long_click(*pos, duration=duration)
        except TargetNotFoundError as e:
            raise e

    @delay
    def pic_swipe(self, img1: str | tuple, img2: str | tuple, *, duration: float = 0.5):
        """
        图片滑动操作
        :param img1: 起始图片路径或坐标元组
        :param img2: 终点图片路径或坐标元组
        :param duration: 滑动持续时间，每个步骤的执行被限制为每步5ms
        :return: 无
        """

        if isinstance(img1, str):
            v1 = Template(img1)
            try:
                pos1 = self.loop_find(v1, timeout=ST.FIND_TIMEOUT)
            except TargetNotFoundError as e:
                raise e
        else:
            pos1 = img1

        if isinstance(img2, str):
            v2 = Template(img2)
            try:
                pos2 = self.loop_find(v2, timeout=ST.FIND_TIMEOUT_TMP)
            except TargetNotFoundError as e:
                raise e
        else:
            pos2 = img2
        lg.info("位置1:%s, 位置2：%s", pos1, pos2)
        self.swipe(*pos1, *pos2, duration=duration)
